#ifndef __CAMERA_H__
#define __CAMERA_H__

#include "SceneObject.h"

namespace Nezha
{

	typedef Rectf ViewPortf;

	enum FrustumPlane
	{
		FP_LEFT = 0,
		FP_RIGHT,
		FP_TOP,
		FP_BOTTOM,
		FP_NEAR,
		FP_FAR,

		FP_NUM
	};

	struct GRAPHICS_ENTRY Frustum
	{
		Frustum(const ViewPortf& vp, float _near, float _far)
		{
			mLeft = vp.LeftCorner.x;
			mRight = vp.RightCorner.x;
			mTop = vp.LeftCorner.y;
			mBottom = vp.RightCorner.y;
			mNear = _near;
			mFar = _far;
		}

		Frustum()
			:mLeft(0)
			,mRight(0)
			,mTop(0)
			,mBottom(0)
			,mNear(0)
			,mFar(0)
		{
		}

		// TODO
		//static Plane GetPlane(const Frustum& f, FrustumPlane ft);

		float mLeft;
		float mRight;
		float mTop;
		float mBottom;
		float mNear;
		float mFar;
	};

	enum DepthType
	{
		DT_DX = 0,		// 0, 1
		DT_OGL,			// -1, 1
	};

	/**
	@brief
		Default camera:
			Eye = 0, 0, 0
			Direction = 0, 0, -1
			Up = 0, 1, 0
			Right = 1, 0, 0

	@remarks
		Camera is invalid before be binded to a scene node.
	*/
	class GRAPHICS_ENTRY Camera : public SceneObject
	{
	public:
		Camera(const _string& name);
		Camera(const _string& name, bool perspective);

		virtual ~Camera();

		// override this for correctly runtime type info.
		virtual int getSceneObjectType() const
		{
			return SOT_CAMERA;
		}

		bool getPerspective() const
		{
			return mPerspective;
		}

		void setFrame(const Vector3& position, const Vector3& dir, const Vector3& up, const Vector3& right);

		void setAxes(const Vector3& dir, const Vector3& up, const Vector3& right);

		void setFrustum(const Frustum& f);

		/**
		@REMARKS
			fovY : degree.
		*/
		void setFrustum(float fovY, float aspect, float _near, float _far);

		bool windowPoint2Ray(int x, int y, Vector3& rayOrig, Vector3& rayDir);

		Vector3 getDirection() const
		{
			return mDirection;
		}

		Vector3 getUp() const
		{
			return mUp;
		}

		Vector3 getRight() const
		{
			return mRight;
		}

		const Frustum& getFrustum() const
		{
			return mFrustum;
		}

		const Matrix4x4& getViewMat() const
		{
			return mViewMat;
		}

		const Matrix4x4& getProjectionMat() const
		{
			return mProjectionMat;
		}

		const Matrix4x4& getProjectionViewMat() const
		{
			return mPVMat;
		}

		static DepthType GetDepthType();

		void setCullingMask(u32 cullingMask);
		u32 getCullingMask() const
		{
			return mCullingMask;
		}

NEZHA_INTERNAL:
		void _updateViewMatrix();
		void _updateProjectionMatrix();
		void _setPerspective(bool b);

		// only dispacthed by renderer implement
		static void _SetDepthType(DepthType dt);
	protected:
		bool mPerspective;

		// frame
		Vector3 mDirection;
		Vector3 mUp;
		Vector3 mRight;

		Frustum mFrustum;

		// Matrixs
		Matrix4x4 mProjectionMat;
		Matrix4x4 mPVMat;
		Matrix4x4 mViewMat;

		static DepthType msDepthType;

		u32 mCullingMask;
	};

	typedef RefCountedPtr<Camera>::Default CameraPtr;

}//end namespace Nezha

#endif //end __CAMERA_H__